<?
/*! \brief Route requests delivered through the backend.
 * 
 */
class ServiceController extends BaseServiceController
{	
	/*! \brief Default action. Start Here.
	 * 
	 * No service will display available services, else we will validate the
	 * POST data against the service XML and be forwarded to executeAction on 
	 * success or usageAction on failure.
	 */
	public function indexAction()
	{
		//if theres no service, then display the available services, otherwise
		if(isset($this->_service))
		//we will process the POST
		{		
			//If the POST data is invalid, render usage, otherwise execute.
			if(!$this->_validator->isValid())
				$this->_forward('usage');
			else
				$this->_forward('execute');
		}
		else
			$this->getResponse()->setHeader('Content-Type', 'text/html');
		
	}
	
	/*! \brief Return usage information.
	 * 
	 * Return XML representation of the service from which usage information 
	 * can be deduced.
	 */
	public function usageAction()
	{	
		//display service XML, we call this "usage"
		$this->view->xml = $this->_service->getXml();
	}
	
	/*! \brief Balance load and execute the web service.
	 * 
	 * Checks for node status: ROOT, MASTER, SLAVE. ROOT will get the average 
	 * load of a cluster from a MASTER. MASTER will retrieve the SLAVE with 
	 * least load. SLAVE will execute the command. Any result will bubble up to 
	 * ROOT.
	 */
	public function executeAction() 
	{	
		if($this->_method == 'reportload')
			$loadReport = true;

		//We don't load balance for load reports
		if(ROOT && $this->getRequest()->get('root') != HOST_NAME && 
			!$loadReport)
		{
			$this->_helper->viewRenderer->setNoRender();
			$load_balancer = new LoadBalancer();
			$load_balancer->refreshClusterLoads();						
			$cluster = $load_balancer->getClusterLeastLoad();

			$url = $cluster.'/Service/'.$this->_method;			
			$client = new Zend_Http_Client($url, array('timeout' => 3600, 'keepalive' => true));
			$client->setParameterPost($this->getRequest()->getPost());
			$client->setParameterPost('root', HOST_NAME);

			$client = $this->repostFiles($client);
			$this->sendRequest($client);
	
			//$response = $this->sendRequest($client);
			//$this->getResponse()->setBody($response->getBody());
		}
		elseif(MASTER && $this->getRequest()->get('master') != HOST_NAME && 
			!$loadReport)
		{
			$this->_helper->viewRenderer->setNoRender();
			$load_balancer = new LoadBalancer();
			$load_balancer->refreshSlaveLoads();
			$slave = $load_balancer->getSlaveLeastLoad();
			
			$url = $slave.'/Service/'.$this->_method;
			
			$client = new Zend_Http_Client($url, array('timeout' => 3600, 'keepalive' => true));
			$client->setParameterPost($this->getRequest()->getPost());
			$client->setParameterPost('master', HOST_NAME);
			
			$client = $this->repostFiles($client);
			$this->sendRequest($client);
			//$response = $this->sendRequest($client);
			//$this->getResponse()->setBody($response->getBody());
		}
		else
		{
			$result = $this->_service->executeCommand();
			
			if($this->_service->getExitCode() > 0)
			{
				$this->view->error = true;
				$this->view->result = $this->_service->getErrorAsXML();
				$content_type = "text/xml";
			}
			else
			{
				$this->view->error = false;
				$this->view->result = $result;
				$content_type = "application/octet-stream";
			}
			
			$this->view->dir = $this->_service->getRootDir();		
			$fname = $this->_service->getOutputFile();
			
			//headers	
			$this->getResponse()->setHeader('fname', "$fname");
			$this->getResponse()->setHeader('md5', md5($result));
			$this->getResponse()->setHeader('Content-Type', $content_type);
			$this->getResponse()->setHeader('Content-Transfer-Encoding', 'binary');
			$this->getResponse()->setHeader('Content-Length', filesize($result));
		}
	}

	/*! \brief Web service for listing available web services.
	 * 
	 * Calls the static method ServiceManager::getServices() and returns the
	 * xml response.
	 * 
	 * \return text/xml
	 */
	public function queryAction()
	{
		//get a list of services from the ServiceManager
		$this->view->xml = ServiceManager::getServices();
	}
	
	/*! \brief Web service to register a MASTER => ROOT or SLAVE => MASTER
	 * 
	 * \param GET['username'] a valid user
	 * \param GET['password'] a valid password
	 * \param GET['ip'] the IP address of the machine wanting to register
	 * \param GET['type'] the type of the node to register e.g., MASTER or SLAVE 
	 */
	public function registerAction()
	{
		$data = array(	'username' 	=> $this->getRequest()->get('username'),
						'password'	=> $this->getRequest()->get('password'),
						'ip'		=> $this->getRequest()->get('ip'),
						'type'		=> $this->getRequest()->get('type'));
				
		switch($data['type'])
		{
			case 'MASTER':
				$root = new Root($this->_db);
				$root->localRegister($data);
				break;
			case 'SLAVE':
				$master = new Master($this->_db);
				$master->localRegister($data);
				break;
			default:
				Logger::file()->err('Unrecognized registration type');
				break;
		}
	}
	
	/*! \brief Get this node to report a load
	 * 
	 * \param GET['type'] is MASTER or SLAVE
	 * \return float load 
	 */
	public function reportloadAction()
	{
		$type = $this->getRequest()->get('type');
		
		$load = -1;
		switch(strtoupper($type))
		{
			case 'MASTER':
				$master = new Master();
				$load = $master->getLocalLoad();
				break;
			case 'SLAVE':
				$slave = new Slave();
				$load = $slave->getLocalLoad();
				break;
			default:
				Logger::file()->err('Unrecognized load report type');
				break;
		}
		
		echo $load;
	}
	
	/*! \brief RePOST files from one machine to another
	 * \param Zend_Http_Client to redirect to
	 * \return Zend_Http_Client
	 */
	public function repostFiles($client)
	{
		foreach($_FILES as $key=>$value)
		{			
			if($_FILES[$key]['size'] > 0 )
				$client->setFileUpload($_FILES[$key]['name'], $key, 
					file_get_contents($_FILES[$key]['tmp_name']));
		}
		
		return $client;
	}
	
	/*! \brief Send request or "execute" the web service
	 * 
	 * \param Zend_Http_Client prepared and ready
	 * \return binary/octet-stream body of the client response 
	 */
	public function sendRequest($client)
	{
		//get http client response
		$response = $client->request('POST');

		//propogate headers
		$this->getResponse()->setHeader('Content-Type', $response->getHeader('Content-Type')); 
		$this->getResponse()->setHeader('Transfer-Encoding', $response->getHeader('Transfer-Encoding')); 
		$this->getResponse()->setHeader('fname', $response->getHeader('fname'));
		$this->getResponse()->setHeader('md5', $response->getHeader('md5'));
		$this->getResponse()->setHeader('Content-Length', $response->getHeader('Content-Length'));
		$this->getResponse()->setHeader('Connection', 'close');
		$this->getResponse()->setBody($response->getBody());
		
		return true;
	}
}
